﻿// #define TEST_DETAIL_PAGE
#if !DEBUG && TEST_DETAIL_PAGE
Remove TEST_DETAIL_PAGE flag for RELEASE
#endif

using EmperialApps.WeatherSpark.Data;
using EmperialApps.WeatherSpark.Helpers;
using EmperialApps.WeatherSpark.Internal;
using EmperialApps.WeatherSpark.Resources;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;

namespace EmperialApps.WeatherSpark {

    using SystemGestureEventArgs = System.Windows.Input.GestureEventArgs;

    public partial class SummaryPage : PhoneApplicationPage {

        private readonly IApplicationBar _appBar;
        private readonly ApplicationBarIconButton _addIconButton;
        private readonly ForecastDownloadManager _downloadManager;
        private readonly List<LocationSettings> _pendingUpdates;
        private readonly TrialManager _trial;
        private LocationSettings _selected;
        private ContextMenu _lastTileMenu;
        private bool? _showManageHelpText;
        private Button _rebuildTilesButton;
        private bool _rebuildingTiles;


        static SummaryPage( ) {
            TiltEffect.TiltableItems.Add( typeof( WeatherSummary ) );
        }

        public SummaryPage( ) {
            typeof( SummaryPage ).Log( "Initializing" );
            this.RegisterTranstions( );
            this.InitializeComponent( );

            this._appBar = this.ApplicationBar;
            this._addIconButton = this._appBar.AddIconButton( Localized.Button.add, this.OnAddClicked, isEnabled: true );
            this._appBar.AddMenuItem( Localized.Menu.about, this.NavigateTo( Settings.AboutPage ) );

            this.UpdateApplicationBar( Settings.Count );

            this._downloadManager = new ForecastDownloadManager( this, SetDownloadProgress );
            this._downloadManager.ForecastDownloadCompleted += this.OnForecastDownloadCompleted;
            this._pendingUpdates = new List<LocationSettings>( Settings.GetAllLocationSettings( ) );
            this._trial = new TrialManager( this );
        }


        private bool ShowManageHelpText {
            get {
                if( !this._showManageHelpText.HasValue )
                    this._showManageHelpText = Settings.IsFirstForecast( );

                return this._showManageHelpText.Value
                    && Settings.Count == 1;
            }
            set {
                bool change = value != this.ShowManageHelpText;
                this._showManageHelpText = value;
                if( change )
                    this.UpdateForecasts( );
            }
        }

        private LocationSettings Selected {
            get {
                Settings.Current = this._selected;
                return this._selected;
            }
            set {
                Settings.Current = value;
                this._selected = value ?? this._selected;
            }
        }

        private Button RebuildTilesButton {
            get {
                if( this._rebuildTilesButton == null ) {
                    Style rebuildButtonStyle = (Style)this.TileContainer.Resources["RebuildButtonStyle"];
                    var refreshImage = new BitmapImage( "refresh".ToImageUri( ) ) { CreateOptions = BitmapCreateOptions.None };
                    this._rebuildTilesButton = new Button {
                        Style = rebuildButtonStyle,
                        Content = AppResources.SummaryPage_RebuildTiles,
                        Background = new ImageBrush { ImageSource = refreshImage, Stretch = Stretch.Fill }
                    };
                    this._rebuildTilesButton.Click += OnRebuildTilesClicked;
                }

                return this._rebuildTilesButton;
            }
        }


        protected override void OnNavigatedTo( NavigationEventArgs e ) {
            base.OnNavigatedTo( e );
            LittleWatson.CheckForPreviousException( );

            if( this._rebuildingTiles )
                this.RebuildTiles( );
            else
                this.UpdateForecasts( );
#if !TEST_DETAIL_PAGE
            this.OnForecastDownloadCompleted( null, null );
        }
#else
            if( this._navigate ) {
                this._navigate = false;
                this.Selected = Settings.GetLocationSettings( 0 );
                this.NavigationService.Navigate( Settings.DetailPage );
            }
        }

        private bool _navigate = true;
#endif

        protected override void OnNavigatedFrom( NavigationEventArgs e ) {
            typeof( SummaryPage ).Log( "Leaving" );

            foreach( UIElement tile in this.TileContainer.Children ) {
                var tileMenu = ContextMenuService.GetContextMenu( tile );
                if( tileMenu != null )
                    tileMenu.IsOpen = false;
            }

            base.OnNavigatedFrom( e );
            this._downloadManager.Cancel( );
        }

        private static void SetDownloadProgress( PhoneApplicationPage page, ProgressKind kind, Localized.Status status, Localized.FormatArgument args ) {
            if( !kind.HasFlag( ProgressKind.Complete ) )
                page.SetProgress( kind, status, args );
        }

        private void RebuildTiles( ) {
            this._rebuildingTiles = false;

            bool anyRebuilt = false;
            for( int i = 0; i < Settings.Count; ++i ) {
                var locationSettings = Settings.GetLocationSettings( i );
                if( locationSettings.HasValidRecoverySettings( ) )
                    continue;

                // If we have already rebuilt one tile, wait until the next activation to run again.
                if( anyRebuilt ) {
                    this._rebuildingTiles = true;
                    return;
                }

                anyRebuilt = true;
                locationSettings.RemoveTile( );
                locationSettings.UpdateTile( null, TileUpdateReason.CreateTile );
            }

            this.TileContainer.Children.Remove( this.RebuildTilesButton );
        }

        private void OnRebuildTilesClicked( object sender, RoutedEventArgs e ) {
            var result = Localized.Popup.RebuildTiles.Ask( default( string ) );
            if( result == MessageBoxResult.OK )
                this.RebuildTiles( );
        }

        private void OnGraphTapped( object sender, SystemGestureEventArgs e ) {
            typeof( SummaryPage ).Log( "Graph clicked" );

            var graph = (UIElement)sender;
            int index = this.ForecastContainer.Children.IndexOf( graph );
            this.Selected = Settings.GetLocationSettings( index );

            Uri address = Settings.DetailPage.GetIndexedPageAddress( index );
            this.NavigationService.Navigate( address );
        }

        private void OnTileClicked( object sender, RoutedEventArgs e ) {
            if( this.Panorama.SelectedItem != this.TileItem )
                return;

            var tile = (UIElement)sender;
            int index = this.TileContainer.Children.IndexOf( tile );
            this.Selected = Settings.GetLocationSettings( index );
            typeof( SummaryPage ).Log( "Tile " + index + " clicked" );

            var tileMenu = ContextMenuService.GetContextMenu( tile );
            this.UpdateTileMenu( tileMenu );
            tileMenu.IsOpen = true;
        }

        private void OnAddClicked( object sender, EventArgs e ) {
            typeof( SummaryPage ).Log( "Add button clicked" );

            if( App.IsTrial && Settings.Count >= Settings.MaximumTrialCount ) {
                Localized.Popup.TrialLimitReached.Show( );
            }
            else {
                this.Selected = null;
                this.NavigationService.Navigate( Settings.ChooseLocationPage );
            }
        }

        private void OnViewClicked( object sender, RoutedEventArgs e ) {
            typeof( SummaryPage ).Log( "View menu clicked" );

            this.ShowManageHelpText = false;
            Uri address = Settings.DetailPage.GetIndexedPageAddress( this.Selected.GetIndex( ) );
            this.NavigationService.Navigate( address );
        }

        private void OnSettingsClicked( object sender, RoutedEventArgs e ) {
            typeof( SummaryPage ).Log( "Settings menu clicked" );

            this.ShowManageHelpText = false;
            Uri address = Settings.SettingsPage.GetIndexedPageAddress( this.Selected.GetIndex( ) );
            this.NavigationService.Navigate( address );
        }

        private void OnDeleteClicked( object sender, RoutedEventArgs e ) {
            typeof( SummaryPage ).Log( "Delete menu clicked" );

            this.ShowManageHelpText = false;
            var result = Localized.Popup.DeleteForecast.Ask( this.Selected.GetDisplayName( ) );
            if( result == MessageBoxResult.OK ) {
                this._pendingUpdates.Remove( this.Selected );
                Settings.RemoveLocation( this.Selected );
                this.Selected = null;

                this.UpdateForecasts( );
            }
        }


        private void OnForecastDownloadCompleted( object sender, ForecastEventArgs e ) {
            // If the last forecast update failed, stop updating.
            if( e != null && e.Error != null )
                return;


            // If we have an updated forecast, apply it to the corresponding graph.
            if( e != null && e.Forecast != null ) {
                var tile = this.TileContainer.Children
                    .OfType<Button>( )
                    .Select( b => (TileOption)b.Content )
                    .FirstOrDefault( t => t != null && t.LocationSettings == this._downloadManager.ForecastSettings );
                if( tile != null )
                    tile.Forecast = e.Forecast;

                var graph = this.ForecastContainer.Children
                    .OfType<WeatherSummary>( )
                    .FirstOrDefault( g => g.Tag == this._downloadManager.ForecastSettings );
                if( graph != null )
                    graph.Forecast = e.Forecast;
                else
                    this.UpdateForecasts( );
            }

            // Start the next automatic forecast update.
            bool updating = false;
            while( !updating && this._pendingUpdates.Count > 0 ) {
                var locationSettings = this._pendingUpdates[0];
                this._pendingUpdates.RemoveAt( 0 );

                updating = this._downloadManager.BeginForecastDownload( locationSettings, locationSettings.DownloadUri, DownloadReason.AutoRefresh, Localized.Status.UpdatingForecast, locationSettings.GetDisplayName( ) );
            }

            // If all forecasts have completed, clear progress.
            if( !updating ) {
                this.SetProgress( ProgressKind.Complete, 0 );
                Settings.UpdateTileAgent( );
            }
        }


        private static void RemoveExtraItems<T>( IList<T> collection, int count ) {
            while( collection.Count > count )
                collection.RemoveAt( count );
        }

        private void UpdateApplicationBar( int count ) {
            this._appBar.IsVisible = count < Settings.MaximumCount;
            this._appBar.Mode =
                count < 4
                    ? ApplicationBarMode.Default
                    : ApplicationBarMode.Minimized;
        }

        private void UpdateTileMenu( ContextMenu tileMenu ) {
            // If the same menu was opened last time, no initialization needed.
            if( tileMenu.Items.Count != 0 )
                return;


            // If this is the first time a menu has been opened, create new menu items.
            if( this._lastTileMenu == null ) {
                var viewItem = new MenuItem { Header = AppResources.menu_view };
                viewItem.Click += this.OnViewClicked;
                tileMenu.Items.Add( viewItem );

                var deleteItem = new MenuItem { Header = AppResources.menu_delete };
                deleteItem.Click += this.OnDeleteClicked;
                tileMenu.Items.Add( deleteItem );

                var settingsItem = new MenuItem { Header = AppResources.menu_settings };
                settingsItem.Click += this.OnSettingsClicked;
                tileMenu.Items.Add( settingsItem );
            }
            // Otherwise, move items to new menu.
            else {
                var items = this._lastTileMenu.Items;
                while( items.Count > 0 ) {
                    var item = items[0];
                    items.RemoveAt( 0 );
                    tileMenu.Items.Add( item );
                }
            }

            this._lastTileMenu = tileMenu;
        }


        private void UpdateForecasts( ) {
            this._trial.Update( );

            int count = Settings.Count;
            this.UpdateApplicationBar( count );
            if( count == 0 ) {
                this.TileItem.Visibility = Visibility.Collapsed;
                this.AddHelpText.Visibility = Visibility.Visible;
                this.ManageHelpText.Visibility = Visibility.Collapsed;
            }
            else {
                this.TileItem.Visibility = Visibility.Visible;
                this.AddHelpText.Visibility = Visibility.Collapsed;
                this.ManageHelpText.Visibility = this.ShowManageHelpText ? Visibility.Visible : Visibility.Collapsed;
            }


            int rowCount = count < 3 ? 1 + count : count;
            var rows = this.ForecastContainer.RowDefinitions;
            var graphs = this.ForecastContainer.Children;
            var tiles = this.TileContainer.Children;
            if( this._rebuildTilesButton != null )
                tiles.Remove( this._rebuildTilesButton );

            RemoveExtraItems( rows, rowCount );
            RemoveExtraItems( graphs, count );
            RemoveExtraItems( tiles, count );

            while( rows.Count < rowCount )
                rows.Add( new RowDefinition { Height = new GridLength( 1, GridUnitType.Star ) } );


            int invalidTileCount = 0;
            for( int i = 0; i < count; ++i ) {
                var locationSettings = UpdateForecast( graphs, tiles, i );
                if( !locationSettings.HasValidRecoverySettings( ) )
                    ++invalidTileCount;
            }

            if( invalidTileCount == 0 ) {
                typeof( SummaryPage ).Log( "All tiles up to date." );
            }
            else {
                typeof( SummaryPage ).Log( invalidTileCount + " tiles out of date." );
                tiles.Add( this.RebuildTilesButton );
            }
        }

        private LocationSettings UpdateForecast( UIElementCollection graphs, UIElementCollection tiles, int i ) {
            var locationSettings = Settings.GetLocationSettings( i );

            FrameworkElement visual;
            FrameworkElement tile;
            Forecast forecast;
            Exception error;
            if( locationSettings.Location.TryLoadForecast( typeof( SummaryPage ), i, out forecast, out error ) ) {
                var graph = this.GetGraph( i );
                InitializeGraph( graph, locationSettings, forecast );
                visual = graph;

                var tileButton = this.GetTile( i );
                InitializeTile( tileButton, locationSettings, forecast );
                tile = tileButton;
            }
            else {
                if( error != null ) {
                    error.Report( Localized.ErrorSource.Loading );
                }
                else {
                    this._downloadManager.BeginForecastRepair( locationSettings );
                    string message = Localized.Format( AppResources.SummaryPage_UnknownForecastLoadFailure, locationSettings.GetDisplayName( ) );
                    error = new Exception( message );
                }

                visual = new ContentControl { Content = error, HorizontalAlignment = HorizontalAlignment.Left };
                tile = new Border( );
            }

            Grid.SetRow( visual, i );
            if( !graphs.Contains( visual ) )
                graphs.Insert( i, visual );

            if( !tiles.Contains( tile ) )
                tiles.Insert( i, tile );

            return locationSettings;
        }

        private WeatherSummary GetGraph( int index ) {
            var graphs = this.ForecastContainer.Children;
            var visual = graphs.ElementAtOrDefault( index );

            var graph = visual as WeatherSummary;
            if( graph == null ) {
                if( index < graphs.Count )
                    graphs.RemoveAt( index );

                graph = new WeatherSummary( );
                graph.Tap += this.OnGraphTapped;
            }

            return graph;
        }

        private Button GetTile( int index ) {
            var tiles = this.TileContainer.Children;
            var visual = tiles.ElementAtOrDefault( index );

            var tile = visual as Button;
            if( tile == null ) {
                if( index < tiles.Count )
                    tiles.RemoveAt( index );

                tile = new Button { CommandParameter = index };
                tile.Click += this.OnTileClicked;
                ContextMenuService.SetContextMenu( tile, new ContextMenu( ) );
            }

            return tile;
        }

        private static void InitializeGraph( WeatherSummary graph, LocationSettings locationSettings, Forecast forecast ) {
            graph.Tag = locationSettings;
            graph.DisplayLevel = locationSettings.GetSummaryPageLevel( );
            graph.Units = locationSettings.Units;
            graph.ShowExtremesUsingNight = locationSettings.NightExtremes;
            graph.Nickname = locationSettings.GetDisplayName( );
            graph.Forecast = forecast;
        }

        private static void InitializeTile( Button tile, LocationSettings locationSettings, Forecast forecast ) {
            var tileOption = new TileOption( locationSettings, forecast );
            tileOption.DisplayMode = (ForecastDisplayMode)Math.Max( 1, (int)locationSettings.Tile );
            tile.Content = tileOption;
        }

    }

}
